package eval.deploy;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cern.jet.random.AbstractDistribution;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import eval.utilities.configuration.ExperimentConfiguration;
import eval.utilities.random.AlternatingDistribution;
import eval.vm.IVMManager;
import eval.vm.VMConfiguration;
import eval.vm.VMIdentifier;
import eval.vm.VirtualMachine;



@Singleton
public class DemandManager implements IDemandManager {

	private ExperimentConfiguration configuration = ExperimentConfiguration.getSingleton();
	
	// maps virtual machines with their demand distribution
	private IVMManager vmManager;
	private Map<VMIdentifier, VMRealDemand> demandTraces = new HashMap<VMIdentifier, VMRealDemand>();
	private Map<VMIdentifier, AbstractDistribution> CPUdemandDistributionMap = new HashMap<VMIdentifier, AbstractDistribution>();
	private Map<VMIdentifier, Integer> MEMdemandDistributionMap = new HashMap<VMIdentifier, Integer>();
	
	private int lowPeriodAverageDuration;
	private int highPeriodAverageDuration;
	
	@Inject
	public DemandManager(IVMManager vmManager) {
		this.vmManager = vmManager;
		this.lowPeriodAverageDuration = configuration.lowPeriodAverageDuration;
		this.highPeriodAverageDuration = configuration.highPeriodAverageDuration;
	}
	
	

	//TODO
	@Override
	public Map<VirtualMachine, Integer> getCPUDemandOfAllVMsAtTick(int tick) {
		Map<VirtualMachine, Integer> allDemands = new HashMap<VirtualMachine, Integer>();
		for (VirtualMachine virtualMachine : vmManager.getAllVirtualMachines()) {
			VMIdentifier vmIdentifier = virtualMachine.getIdentifier();
			VirtualMachine vm = vmManager.getVMbyID(vmIdentifier);
			allDemands.put(vm, new Integer(getCpuDemandOfVmAtTick(vmIdentifier, tick)));
		}
		return allDemands;
	}
	//Test 
	@Override
	public double getTotalCpuemandAtTick(int tick) {
		double toRet = 0.0;
		for (VirtualMachine virtualMachine : vmManager.getAllVirtualMachines()) 
			toRet += getCpuDemandOfVmAtTick(virtualMachine.getIdentifier(), tick);
		return toRet;
	}
	
	@Override
	public double getCpuemandAtTickOfVmList(int x1, List<VirtualMachine> vmList,int tick) {
		double toRet = 0.0;
		for (VirtualMachine virtualMachine : vmManager.getAllVirtualMachines()) 
			if(vmList.contains(virtualMachine))
				toRet += getCpuDemandOfVmAtTick(virtualMachine.getIdentifier(), tick);
		return toRet;
	}
	
	@Override
	public Map<VirtualMachine, Integer> getMEMDemandOfAllVMsAtTick(int tick) {
		Map<VirtualMachine, Integer> allDemands = new HashMap<VirtualMachine, Integer>();
		for (VirtualMachine virtualMachine : vmManager.getAllVirtualMachines()) {
			VMIdentifier vmIdentifier = virtualMachine.getIdentifier();
			allDemands.put(virtualMachine, getMEMDemandOfVmAtTick(vmIdentifier, tick));
		}
		return allDemands;
	}
	
	@Override
	public int getCPUDemandOfVmAtTick(VirtualMachine virtualMachine, int tick) {
		return getCpuDemandOfVmAtTick(virtualMachine.getIdentifier(),tick);
	}

	@Override
	public int getCpuDemandOfVmAtTick(VMIdentifier virtualMachineID, int tick) {
		return getRealDemand(virtualMachineID).getCPUDemandAtTick(tick);
	}
	
	@Override
	public int getMemDemandOfVmAtTick(VirtualMachine virtualMachine, int tick) {
		return getMEMDemandOfVmAtTick(virtualMachine.getIdentifier(),tick);
	}

	@Override
	public int getMEMDemandOfVmAtTick(VMIdentifier virtualMachineID, int tick) {
		return getRealDemand(virtualMachineID).getMEMDemandAtTick(tick);
	}
	
	@Override
	public VMRealDemand getRealDemand(VirtualMachine virtualMachine) {
		return getRealDemand(virtualMachine.getIdentifier());
	}
	//TODO
	@Override
	public VMRealDemand getRealDemand(VMIdentifier virtualMachine) {
		VMRealDemand demandTrace = demandTraces.get(virtualMachine);

		if (demandTrace == null) {
			// demand didn't exists, create a new one
			AbstractDistribution demandDistribution = getDemandDistribution(virtualMachine);
			int memDemand = (vmManager.getVMConfiguration(virtualMachine).getMinMem() +
					vmManager.getVMConfiguration(virtualMachine).getMaxMem() )/2;
			MEMdemandDistributionMap.put(virtualMachine, memDemand);
			demandTrace = new VMRealDemand(demandDistribution,memDemand,
					vmManager.getVMConfiguration(virtualMachine));
			demandTraces.put(virtualMachine, demandTrace);
		}

		return demandTrace;
	}
			
	private AbstractDistribution getDemandDistribution(VMIdentifier virtualMachine) {

		AbstractDistribution demandDistribution = CPUdemandDistributionMap.get(virtualMachine);

		if (demandDistribution == null) {
			// create distribution if it doesn't exist
			VMConfiguration vmConfiguration = vmManager.getVMConfiguration(virtualMachine);
			demandDistribution = generateNewDistribution(vmConfiguration.getMinCpu(), vmConfiguration.getMaxCpu());
			CPUdemandDistributionMap.put(virtualMachine, demandDistribution);
		}

		return demandDistribution;
	}
	
	
	private AbstractDistribution generateNewDistribution(
			int lowPeriodAverageDemand, int highPeriodAverageDemand) {
		// the specific distribution can be changed in the future in this method
		// do it parametric
		return new AlternatingDistribution(lowPeriodAverageDuration,
				lowPeriodAverageDemand, highPeriodAverageDuration,
				highPeriodAverageDemand);
	}



	

}
